home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1996 #14
/
Monster Media No. 14 (April 1996) (Monster Media, Inc.).ISO
/
prog_d
/
oleauttr.zip
/
NESTOBJ.TXT
< prev
next >
Wrap
Text File
|
1996-01-04
|
7KB
|
184 lines
*******************************************************************
Accessing nested OLE object properties with TOLEAutoClient
*******************************************************************
*********************************
NESTED MIRROR CLASSES
*********************************
In cases when an OLE Automation server exposes "nested" objects,
such as in the case of Microsoft Excel and Microsoft Project and
many others, the "mirror" class should be written to contain, as
a member, an instance of the sub-object's mirror class.
Refer to the example and sample code in the accompanying file
GRAPHOLE.ZIP for a simple illustration of the approach, using
Microsoft's MSGRAPH as the server. Other accompanying example
files also give additional illustrative material for reference.
Before any properties or methods of the sub-object can be accessed,
it will be necessary to execute code to construct the sub-object
instance, as described below. For additional information and examples,
refer to the zipped example files supplied. You will need to
consult your server's documentation for information on the
class hierarchy exposed by your server. For Microsoft applications,
the Developer Network CD-ROM is a useful source of such information.
For example, suppose a server whose ID is "Myserver.Document"
exposes one string property called Name, and also contains a sub-object
called Font which in turn has a string property called Color. In this case,
the "mirror" classes might look like this:
interface
uses SysUtils, OleAuto;
type
TDocFont = class(TOleObject)
private
function GetColor : String;
procedure SetColor(str : String);
public
property Color : String read GetColor write SetColor;
end;
TDocument = class(TOleObject)
private
fontFont : TDocFont;
function GetName : String;
procedure SetName(sName : String);
function GetFont : TDocFont;
public
property Name : String read GetName write SetName;
property DocFont : TDocFont read GetFont write fontFont;
end;
implementation
function TDocument.GetName : String;
const
pszName : PChar = 'naaaaaammmmmmmme';
begin
GetOleProperty('Name', 'PChar', pszName);
Result := StrPas(pszName);
end;
procedure TDocument.SetName(sName : String);
const
pszName : PChar = 'naaaaaammmmmmmme';
begin
StrPCopy(pszName, sName);
SetOleProperty('Name', 'PChar', pszName);
end;
function TDocument.GetFont : TDocFont;
var
pIntFont : PInterface;
begin
GetOleProperty('Font', 'pInterface', pIntFont);
fontFont := TDocFont.ConnectInterface(pIntFont);
Result := fontFont;
end;
function TDocFont.GetColor : String;
const
pszColor : PChar = 'collllllooooooooor';
begin
GetOleProperty('Color', 'PChar', sColor);
Result := StrPas(pszColor);
end;
procedure TDocFont.SetColor(sColor : String);
const
pszColor : PChar = 'collllllooooooooor';
begin
StrPCopy(pszColor, sColor);
SetOleProperty('Color', 'PChar', pszColor);
end;
Pay particular attention to the declaration and implementation of
the DocFont property. The property is represented as a private
class member (field) of type TDocFont. Setting the property (its write
access) is accomplished merely by assigning to it, but retrieving the
property (its read access) is accomplished through the TDocument.GetFont
function. This function has the essential side-effect of calling the
TDocFont constructor ConnectInterface, inherited from TOleObject. This
is because before any properties of the DocFont sub-object can be accessed,
it must be constructed using a call to its ConnectInterface constructor,
which takes as its argument the pInterface returned by the call to
GetOleProperty that retrieves the pInterface for the OLE server's
DocFont property.
For some servers, you may need to retrieve the pInterface for
a sub-property or collection by using CallOleFunction instead of
GetOleProperty. Also, the 'pInterface' type defined for this
component may be referred to as 'pDisp', 'LPDISPATCH', 'LPDISP',
or some similar type in your server documentation. All are
equivalent to a pointer to a low-level OLE IDispatch interface.
In order to access the OLE server properties, you first
need to activate the server by calling its mirror class CreateObject
(or GetObject or GetInProcessObject) constructor. Then you can access
the properties in the normal way. A typical code fragment might look like:
var
sMyName, sMyColor : String;
Document1 : TDocument;
begin
Document1 := TDocument.CreateObject('myserver.document');
sMyName := Document1.Name;
sMyColor := Document1.Font.Color;
Document1.Release;
...
end;
*********************************
MEMORY MANAGEMENT - AVOID LEAKAGE
*********************************
Several memory management issues arise when writing mirror classes.
First of all, for simplicity the above example uses Delphi "typed
constants" to hold local string variables. Despite the name given
to them by Object Pascal (i.e., Delphi), these are not constants
at all, but really static variables to which values can be assigned.
While there is nothing really wrong with using them for this purpose,
it is not really good programming practice, since they are allocated
in the application's data segment and thus consume valuable limited
resources. A better approach would be to use dynamic allocation
via, for example, the StrAlloc() function. Of course, each string
allocated in this way must be freed using the StrDispose procedure.
Using this approach, the first function in the above example could
be rewritten as:
function TDocument.GetName : String;
var
pszName : PChar;
begin
pszName := StrAlloc(256); { Get some space. }
GetOleProperty('Name', 'PChar', pszName);
Result := StrPas(pszName);
StrDispose(pszName); { Free the no-longer-needed space. }
end;
Note that, to be absolutely safe, you could protect the
memory allocation within an exception-handling block.
A more subtle memory allocation issue arises when writing
mirror classes for nested objects. Within the mirror class,
when you call a nested class ConnectInterface constructor,
Delphi allocates memory dynamically on the heap for the
members of the instance of that class that it creates.
Unless you explicitly keep track of such instances, and
call their destructors, "memory leaks" within Windows
can occur when your application shuts down. Delphi's
internal memory allocator can sometimes protect you
against this when only a few such constructor calls are
made, but not if they are called repeatedly. The best
practice is to keep track of these yourself within your
mirror class. Fortunately, this is easy to do since code
has already been written for you. One way of doing this
is to write a descendant (or subclass) of TOleObject that
overrides the ConnectInterface constructor in order to
keep track of the relevant object references in a list.
See the code in VISIOOLE.ZIP for an example.